import { Vector3, Matrix } from "@babylonjs/core/Maths/math.vector";
import { BoundingSphere } from "@babylonjs/core/Culling/boundingSphere";
import { ILoose, TypedArray } from "../tool/utils";
import { Calculation, MIN_FLOAT } from '../tool/calculation';

/**
 * 数学三点面
 * @param a 顶点1
 * @param b 顶点2
 * @param c 顶点3
 */
class Face3 {

    private _a: Vector3 = new Vector3();
    private _b: Vector3 = new Vector3();
    private _c: Vector3 = new Vector3();
    private _n: Vector3 = new Vector3();
    private _tV0: Vector3 = new Vector3();
    private _tV1: Vector3 = new Vector3();
    private _needsUpdate: boolean = true;
    private _sphere: BoundingSphere = new BoundingSphere(new Vector3(-MIN_FLOAT, -MIN_FLOAT, -MIN_FLOAT), new Vector3(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT));
    private _userData: ILoose<any> = {};

    constructor(a?: Vector3, b?: Vector3, c?: Vector3) {
        if (a) this._a.copyFrom(a);
        if (b) this._b.copyFrom(b);
        if (c) this._c.copyFrom(c);
    }

    /**
     * 获取顶点1
     */
    public get a(): Vector3 {
        return this._a;
    }

    /**
     * 设置顶点1
     */
    public set a(vec3: Vector3) {
        this._needsUpdate = true;
        this._a.copyFrom(vec3);
    }

    /**
     * 获取顶点2
     */
    public get b(): Vector3 {
        return this._b;
    }

    /**
     * 设置顶点2
     */
    public set b(vec3: Vector3) {
        this._needsUpdate = true;
        this._b.copyFrom(vec3);
    }

    /**
     * 获取顶点3
     */
    public get c(): Vector3 {
        return this._c;
    }

    /**
     * 设置顶点3
     */
    public set c(vec3: Vector3) {
        this._needsUpdate = true;
        this._c.copyFrom(vec3);
    }

    /**
     * 获取法线
     */
    public get normal(): Vector3 {
        if (this._needsUpdate) this.update();
        return this._n;
    }

    /**
     * 获取包围球
     */
    public get sphere(): BoundingSphere {
        if (this._needsUpdate) this.update();
        return this._sphere;
    }

    /**
     * 获取自定义数据对象
     */
    public get userData(): ILoose<any> {
        return this._userData;
    }

    /**
     * 根据值设置其中一个顶点
     * @param component 要设置的顶点（0|'a'：顶点1, 1|'b'：顶点2, 2|'c'：顶点3） 
     * @param x x值
     * @param y y值
     * @param z z值
     * @returns 当前三点面
     */
    public setByValue(component: 0 | 1 | 2 | 'a' | 'b' | 'c', x: number, y: number, z: number): this {
        this._needsUpdate = true;
        switch (component) {
            case 0:
                this._a.set(x, y, z);
                break;
            case 1:
                this._b.set(x, y, z);
                break;
            case 2:
                this._c.set(x, y, z);
                break;
            case 'a':
                this._a.set(x, y, z);
                break;
            case 'b':
                this._b.set(x, y, z);
                break;
            case 'c':
                this._c.set(x, y, z);
                break;
        }
        return this;
    }

    /**
     * 根据索引和集合设置其中一个顶点
     * @param component 要设置的顶点（0|'a'：顶点1, 1|'b'：顶点2, 2|'c'：顶点3） 
     * @param index 要使用的值在集合中的索引（第一个）
     * @param array 值集合
     * @returns 当前三点面
     */
    public setByIndex(component: 0 | 1 | 2 | 'a' | 'b' | 'c', index: number, array: TypedArray | Array<number>): this {
        this._needsUpdate = true;
        let i = index * 3;
        switch (component) {
            case 0:
                this._a.set(array[i], array[i + 1], array[i + 2]);
                break;
            case 1:
                this._b.set(array[i], array[i + 1], array[i + 2]);
                break;
            case 2:
                this._c.set(array[i], array[i + 1], array[i + 2]);
                break;
            case 'a':
                this._a.set(array[i], array[i + 1], array[i + 2]);
                break;
            case 'b':
                this._b.set(array[i], array[i + 1], array[i + 2]);
                break;
            case 'c':
                this._c.set(array[i], array[i + 1], array[i + 2]);
                break;
        }
        return this;
    }

    /**
     * 应用矩阵变换
     * @param mat4 矩阵
     * @returns 当前三点面
     */
    public applyMatrix4(mat4: Matrix): this {
        Vector3.TransformCoordinatesToRef(this._a, mat4, this._a);
        Vector3.TransformCoordinatesToRef(this._b, mat4, this._b);
        Vector3.TransformCoordinatesToRef(this._c, mat4, this._c);
        this._needsUpdate = true;
        return this;
    }

    /**
     * 更新几何体
     */
    public update(): void {
        this._tV0.copyFrom(this._b).subtractInPlace(this._a);
        this._tV1.copyFrom(this._c).subtractInPlace(this._a);
        Vector3.CrossToRef(this._tV0, this._tV1, this._n);
        this._n.normalize();
        Calculation.boundingMaxMin([this._a, this._b, this._c], this._tV0, this._tV1);
        this._sphere.reConstruct(this._tV1, this._tV0);
        this._needsUpdate = false;
    }

    /**
     * 从其它三点面拷贝
     * @param face 
     * @returns 当前三点面
     */
    public copy(face: Face3): this {
        this._a.copyFrom(face.a);
        this._b.copyFrom(face.b);
        this._c.copyFrom(face.c);
        this._n.copyFrom(face.normal);
        this._needsUpdate = face._needsUpdate;
        return this;
    }

    /**
     * 克隆当前三点面
     * @returns 新的三点面
     */
    public clone(): Face3 {
        let c = new Face3();
        c.copy(this);
        return c;
    }
}


export { Face3 };